package com.zhang.checker.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.alibaba.fastjson2.JSON;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import com.zhang.checker.IJwtService;
import com.zhang.checker.Resolver;
import com.zhang.checker.TokenGenerator;
import com.zhang.constants.UserConstant;
import com.zhang.principal.UserInfo;
import com.zhang.principal.impl.SimpleUserInfo;
import com.zhang.service.RedisService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


public class JwtService implements IJwtService {

    @Autowired
    RedisService redisService;


    /**
     * 签名密钥
     */
    private final String keySecret;


    /**
     * 生成算法
     */
    private final String algorithm;


    public JwtService(String keySecret,String algorithm) {
        this.keySecret = keySecret;
        this.algorithm = algorithm;
    }

    /**
     * @return 创建Token
     */
    public String createToken(UserInfo userInfo) {

        try {
            MACSigner signer = new MACSigner(keySecret);
            //创建jwtBuilder
            JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
            //设置jwt过期时间
            builder.expirationTime(new Date(System.currentTimeMillis()+ TimeUnit.SECONDS.toMillis(UserConstant.EXPIREIN)));
            //设置用户ID
            builder.claim(UserConstant.CLAIM_PRINCIPAL_ID,userInfo.getId());
            //设置加密后的用户角色信息
            String RoleStr = Base64.encode(decryptRole(userInfo));
            builder.claim(UserConstant.CLAIM_PRINCIPAL_ROLES,RoleStr);
            //添加更多信息
            addMoreInfo(userInfo, builder);
            //添加jwt头部算法，构建签署完成的jwt
            SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.parse(algorithm)), builder.build());

            signedJWT.sign(signer);

            //jwt序列化
            return signedJWT.serialize();
        } catch (Exception e) {
            throw new AuthorizationException(e);
        }
    }


    /**
     * 解析Token为{@link SimpleUserInfo} 不包含权限信息，状态
     */
    public UserInfo resolve(String token) throws AuthenticationException{
        if (!StringUtils.hasLength(token)){
            throw new AuthenticationException("Token不能为空");
        }

        try {
            SignedJWT signedJWT = SignedJWT.parse(token);
            //验证jwt是否被修改
            JWSVerifier verifier = new MACVerifier(keySecret);
            signedJWT.verify(verifier);
            //获取claim
            JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
            Map<String, Object> claims =  claimsSet.getClaims();

            SimpleUserInfo userInfo = new SimpleUserInfo();
            //设置用户ID
            userInfo.setId((String) claims.get(UserConstant.CLAIM_PRINCIPAL_ID));
            //设置主体角色信息
            String byteStr = (String) claims.get(UserConstant.CLAIM_PRINCIPAL_ROLES);
            encryptAndSetRole(userInfo,Base64.decode(byteStr));

            //......添加额外的信息

            return userInfo;
        } catch (ParseException | JOSEException e) {
            throw new AuthenticationException("Token验证失败");
        }
    }

    /**
     * 添加额外的信息到jwt
     * @param userInfo
     * @param builder
     */
    private static void addMoreInfo(UserInfo userInfo, JWTClaimsSet.Builder builder) {
        Map<String, Object> externInfo = userInfo.getExternInfo();
        builder.claim(UserConstant.USER_NICK_NAME,externInfo.get(UserConstant.USER_NICK_NAME));
        builder.claim(UserConstant.USER_PHONE,externInfo.get(UserConstant.USER_PHONE));
    }

    /**
     * 使用私钥加密角色信息
     */
    private byte[] decryptRole(UserInfo userInfo){
        RSA rsa = redisService.getRsa();
        return rsa.encrypt(JSON.toJSONString(userInfo.getRoles()), KeyType.PublicKey);
    }

    /**
     * 使用公钥解密角色信息
     */
    private void encryptAndSetRole(UserInfo userInfo,byte[] encrypt){
        RSA rsa = redisService.getRsa();
        byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
        //设置主体角色
        userInfo.setRoles(JSON.parseObject(decrypt, Set.class));
    }





    /**
     * @return 获取用户的所有角色
     */
    public Set<String> getRoles() {
        return null;
    }
}
